/****************************************************************************************
**  File: Framer.hpp
**  Author: Asteroth
**  Date: Apr 27, 2009
****************************************************************************************/

#ifndef __FRAMER_HPP_
#define __FRAMER_HPP_

#define FRAMER_CONTINUE 0
#define FRAMER_EXIT     1

class Framer
{
    private:
        int UPS, DPS;
        int rUPS, rDPS, rSLP;

        int rcUPS, rcDPS, lastPS;

    public:
        Framer()
        {
            UPS = 25; DPS = 25;
            rUPS = UPS; rDPS = DPS;
        }

        Framer(int ups, int dps)
        {
            UPS = ups>1 ? ups : 2; DPS = dps>1 ? dps : 2;
            rUPS = UPS; rDPS = DPS;
        }

        virtual ~Framer()
        {
        }

        /// Initialize is called once at the beggining.
        virtual int initialize() = 0;
        /// Update is called every time the interval of update time finishes. The cycle
        /// finishes when update or draw returns FRAMER_EXIT.
        virtual int update() = 0;
        /// Draw is called every time the interval of draw time finishes. The cycle
        /// finishes when update or draw returns FRAMER_EXIT.
        virtual int draw() = 0;
        /// Finalize is called once at the ending.
        virtual int finalize() = 0;

        inline void setUPS(int ups)
        {
            UPS = ups>1 ? ups : 2;
            rUPS = UPS;
        }

        inline void setDPS(int dps)
        {
            DPS = dps>1 ? dps : 2;
            rDPS = DPS;
        }

        inline int getUPS() {return UPS;}
        inline int getDPS() {return DPS;}
        inline int getRealUPS() {return rUPS;}
        inline int getRealDPS() {return rDPS;}

        /// Execute is the core function, it calls initialize then calls update and draw
        /// every interval until one of them returns FRAMER_EXIT, then calls finalize.
        int execute()
        {
            clock_t clk;
            bool goOn = true;
            float luTime = 0, ldTime = 0;
            float uInterval, dInterval;
            int ret = FRAMER_CONTINUE;

            lastPS = 0;
            rcUPS = rcDPS = 0;

            ret = initialize();
            goOn = ret == FRAMER_CONTINUE;

            while (goOn)
            {
                uInterval = 1000.0/(float)UPS;
                dInterval = 1000.0/(float)DPS;

                clk = eClock();

                if (clk > luTime + uInterval)
                {
                    luTime += uInterval;
                    rcUPS++;

                    ret = update();
                    goOn = ret == FRAMER_CONTINUE;
                }
                else if (clk > ldTime + dInterval)
                {
                    ldTime += dInterval;
                    rcDPS++;

                    ret = draw();
                    goOn = ret == FRAMER_CONTINUE;
                }
                else
                    eSleep(1);


                if (lastPS < clk/1000)
                {
                    lastPS = clk/1000;
                    rUPS = rcUPS;
                    rDPS = rcDPS;
                    rcUPS = rcDPS = 0;
                }
            }

            ret = finalize();

            return ret;
        }

};

#endif
